<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>es5 最终继承的写法</title>
  <!-- 
  在 JavaScript ES5 中，最终继承（组合继承）结合了原型链继承和借用构造函数的优点。虽然这种方式很常用，但确实存在调用父类构造函数两次的问题，可能导致性能开销。
  1. 组合继承的实现
  组合继承的基本实现步骤如下：
   1.1 定义父类构造函数
  function Parent(name) {
      this.name = name; // 实例属性
  }

  Parent.prototype.sayHello = function() {
      console.log("Hello, " + this.name);
  };
   1.2 定义子类构造函数
  function Child(name, age) {
      Parent.call(this, name); // 借用构造函数
      this.age = age; // 子类特有的属性
  }
   1.3 继承父类的方法
  Child.prototype = Object.create(Parent.prototype);
  Child.prototype.constructor = Child; // 修正构造函数指向
  2. 调用父类构造函数两次的地方
  在组合继承中，父类构造函数会被调用两次：

  1. 第一次调用：在子类构造函数中，通过  `Parent.call(this, name);`  直接调用父类构造函数。这是为了初始化子类实例的属性。
    
  2. 第二次调用：在  `Child.prototype = Object.create(Parent.prototype);`  中， `Object.create()`  会创建一个新的对象，并将其原型设置为  `Parent.prototype` 。这个过程实际上不会调用父类构造函数，但由于  `Object.create()`  创建的对象是父类的实例，因此在父类构造函数中定义的实例属性不会被初始化。

  3. 性能开销
  由于父类构造函数被调用两次，可能造成以下性能开销：

  - 内存占用：每次调用父类构造函数都会创建新的实例属性，这可能导致不必要的内存使用。
  - 性能消耗：如果父类构造函数执行了复杂的逻辑，重复调用可能会导致性能下降。

  4. 解决方案
  为了避免调用父类构造函数两次，可以使用以下方法：

  - 只调用一次构造函数：可以通过将父类构造函数的逻辑封装在一个单独的方法中，避免在原型链上创建新的实例。

  例如，可以使用一个单独的函数来初始化实例属性：
  function Parent(name) {
      this.name = name;
  }

  Parent.prototype.sayHello = function() {
      console.log("Hello, " + this.name);
  };

  function Child(name, age) {
      // 只调用一次构造函数
      Parent.call(this, name); // 借用构造函数
      this.age = age; // 子类特有的属性
  }

  // 继承父类的方法
  Child.prototype = Object.create(Parent.prototype);
  Child.prototype.constructor = Child;

  // 添加子类特有的方法
  Child.prototype.sayAge = function() {
      console.log("I am " + this.age + " years old.");
  };

  // 创建实例
  var childInstance = new Child("Alice", 10);
  childInstance.sayHello(); // 输出: Hello, Alice
  childInstance.sayAge();   // 输出: I am 10 years old.
  5. 总结
  组合继承是一种常见的继承方式，但由于父类构造函数被调用两次，可能导致性能开销。理解这一点对于优化代码和提高性能非常重要。通过合理设计构造函数和继承方式，可以有效避免不必要的性能损失。
   -->
</head>
<body>

  <script>
    // 创建对象过程
    function createObject(o) {
      function F() {}
      F.prototype = o;
      return new F();
    }

    // 将Subtype和Supertype联系在一起
    // 寄生式函数
    function inheritPrototype(Subtype, Supertype) {
      // Subtype.prototype.__proto__ = Supertype.prototype
      // Object.setPrototypeOf(Subtype.prototype, Subtype.prototype)
      Subtype.prototype = createObject(Supertype.prototype);
      Object.defineProperty(Subtype.prototype, 'constructor', {
        enumerable: false,
        configurable: true,
        writable: true,
        value: Subtype
      })
      Object.setPrototypeOf(Subtype, Supertype);
      // Subtype.__proto__ = Supertype
    }
  </script>

  <script>
    // 寄生组合式继承
    // 原型链/借用/原型式(对象之间)/寄生式函数
    function Person(name, age, height) {
      this.name = name;
      this.age = age;
      this.height = height;
    }
    Person.prototype.sayName = function() {
      console.log(this.name);
    }
    Person.prototype.sayAge = function() {
      console.log(this.age);
    }

    function Student(name, age, height, grade, sno, score) {
      Person.call(this, name, age, height);
      this.grade = grade; // 借用构造函数
      this.sno = sno;
      this.score = score;
    }
    inheritPrototype(Student, Person);
    Student.prototype.sayGrade = function() {
      console.log(this.grade);
    }

    // 创建实例对象
    const stu1 = new Student('张三', 18, 1.88, '高一', 1001, 99);
    console.log(stu1);
    stu1.sayName();
    stu1.sayAge();
    stu1.sayGrade();
  </script>
  
</body>
</html>